package com.example.springboottools.utils;


import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author SH102909
 */
public class DateUtils {

    public static final String YMD = "yyyyMMdd";
    public static final String YYDDMMHHMMSS = "yyyy-MM-dd HH:mm:ss";
    public static final String YMD_SLASH = "yyyy/MM/dd";
    public static final String YMD_DASH = "yyyy-MM-dd";
    public static final String YMD_DASH_WITH_TIME = "yyyy-MM-dd H:m";
    public static final String YYMMDDHHMM = "yyyy-MM-dd HH:mm";
    public static final String YDM_SLASH = "yyyy/dd/MM";
    public static final String YDM_DASH = "yyyy-dd-MM";
    public static final String HM = "HHmm";
    public static final String HM_COLON = "HH:mm";
    public static final String HMS = "HHmmss";
    public static final String YMDHMS = "yyyyMMddHHmmss";
    public static final String YMD_CHIN="yyyy年MM月dd日";
    public static final long DAY = 24 * 60 * 60 * 1000L;
    public static final long Hour =  60 * 60 * 1000L;
    public static final long year = 24 * 60 * 60 * 365 * 1000L;

    private static final Map<String, DateFormat> DFS = new HashMap<String, DateFormat>();

    /**
     * 检验时间字符串格式
     * @param dateStr 时间字符串
     * @return 是否符合格式
     */
    public static boolean checkDate(String dateStr,String formatStr) {
        if (StringUtils.isBlank(formatStr))formatStr=DateUtils.YMD_DASH;
        SimpleDateFormat format = new SimpleDateFormat(formatStr);
        try {
            Date date = format.parse(dateStr);
            return dateStr.equals(format.format(date));
        } catch (ParseException e) {
            return false;
        }
    }

    public static boolean isDate(String date)
    {
        /**
         * 判断日期格式和范围
         */
        String rexp = "^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))";

        Pattern pat = Pattern.compile(rexp);

        Matcher mat = pat.matcher(date);

        boolean dateType = mat.matches();

        return dateType;
    }

    private DateUtils() {
    }
    public  static String getString(String source, String pattern){
       Integer integer=str2UnixTimestamp(source,YMD);
       String string=formatUnixTimestamp(integer,pattern);
      return string;
    }
    public  static String getString(String source,String sourcePattern, String pattern){
       if (StringUtils.isBlank(sourcePattern))return null;
        Integer integer=str2UnixTimestamp(source,sourcePattern);
       String string=formatUnixTimestamp(integer,pattern);
      return string;
    }
    public static DateFormat getFormat(String pattern) {
        DateFormat format = DFS.get(pattern);
        if (format == null) {
            format = new SimpleDateFormat(pattern);
            DFS.put(pattern, format);
        }
        return format;
    }
    /**
     * 判断月份
     * @param date
     * @return
     */
    public static Boolean dateIsPassMonth(String date) {
        //年月日的正则表达式，此次没有理会2月份闰年
        String regex  = "^((19|20)[0-9]{2})-((01|02|03|04|05|06|07|08|09|10|11|12))$";
        //开始判断,且符合正则表达式
        if(date.matches(regex)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     *
     * @param source
     * @param pattern
     * @return
     */
    public static Date parse(String source, String pattern) {
        if (source == null) {
            return null;
        }
        Date date;
        try {
            // 高并发时会报错 java.lang.NumberFormatException: For input string: ""
            // date = getFormat(pattern).parse(source);

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
            date = simpleDateFormat.parse(source);
        } catch (ParseException e) {
            return null;
        }
        return date;
    }

    public static String format(Date date, String pattern) {
        if (date == null) {
            return null;
        }
        return DateUtil.format(date,pattern);
    }

    private static Calendar convert(Date date) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar;
    }
    /**
     * 获取当天固定时间
     */
    public static Date getCurrentAppointedTime(int hour,int minute,int second){
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, hour); //设置自己今天想得到的点数
        c.set(Calendar.MINUTE, minute);
        c.set(Calendar.SECOND, second);
        return c.getTime();
    }
    /**
     * 获取位移后天的固定时间
     */
    public static Date getOffsetAppointedTime(Date date, int offset,int hour,int minute,int second){
        Calendar c = convert(date);
        c.add( Calendar.DATE, offset);
        c.set(Calendar.HOUR_OF_DAY, hour); //设置自己今天想得到的点数
        c.set(Calendar.MINUTE, minute);
        c.set(Calendar.SECOND, second);
        return c.getTime();
    }
    /**
     * 返回指定天数位移后的日期
     */
    public static Date dayOffset(Date date, int offset) {
        return offsetDate(date, Calendar.DATE, offset);
    }

    /**
     * 返回指定日期相应位移后的日期
     *
     * @param date   参考日期
     * @param field  位移单位，见 {@link Calendar}
     * @param offset 位移数量，正数表示之后的时间，负数表示之前的时间
     * @return 位移后的日期
     */
    public static Date offsetDate(Date date, int field, int offset) {
        Calendar calendar = convert(date);
        calendar.add(field, offset);
        return calendar.getTime();
    }

    /**
     * 返回两个日期间的差异天数
     *
     * @param date1 参照日期
     * @param date2 比较日期
     * @return 参照日期与比较日期之间的天数差异，正数表示参照日期在比较日期之后，0表示两个日期同天，负数表示参照日期在比较日期之前
     */
    public static int dayDiff(Date date1, Date date2) {
        long diff = date1.getTime() - date2.getTime();
        return (int) (diff / DAY);
    }
    /**
     * 返回两个日期间的差异小时
     *
     * @param date1 参照日期
     * @param date2 比较日期
     * @return 参照日期与比较日期之间的天数差异，正数表示参照日期在比较日期之后，0表示两个日期同天，负数表示参照日期在比较日期之前
     */
    public static int hourDiff(Date date1, Date date2) {
        long diff = date1.getTime() - date2.getTime();
        return (int) (diff / Hour);
    }

    /**
     * @Title: getCurrentDate @Description: 返回当前时间 @param @return 设定文件 @return
     * String 返回类型 @throws
     */
    public static String getCurrentDate(String pattern) {
        if (StringUtils.isBlank(pattern)) {
            return format(new Date(), YYDDMMHHMMSS);
        }
        return format(new Date(), pattern);
    }
    /**
     * 返回两个日期间的差异年份
     *
     * @param date1 参照日期
     * @param date2 比较日期
     * @return 参照日期与比较日期之间的天数差异，正数表示参照日期在比较日期之后，0表示两个日期同天，负数表示参照日期在比较日期之前
     */
    public static BigDecimal yearDiff(Date date1, Date date2) {
        try {
            long diff = date1.getTime() - date2.getTime();
            BigDecimal diffBig=new BigDecimal(diff);
            BigDecimal divide = diffBig.divide(new BigDecimal(year),2,BigDecimal.ROUND_HALF_UP);
            return divide;
        }catch (Exception e){
            e.getMessage();
        }
        return null;
    }
    /**
     * 时间字符串增加天数
     *
     * @param dateStr    - 时间字符串
     * @param addDateNum - 增加天数
     * @param formatType - 该字符串格式
     **/
    public static String addDateStr(String dateStr, Integer addDateNum, String formatType) {

        Calendar calendar = new GregorianCalendar();
        calendar.setTime(strToDate(dateStr, null));
        if (addDateNum == null)
            addDateNum = 1;
        calendar.add(Calendar.DATE, addDateNum);
        return dateToStr(calendar.getTime(), null);
    }

    /**
     * Date转换为字符串
     *
     * @param date       - 时间
     * @param formatType - 该字符串格式
     **/
    public static String dateToStr(Date date, String formatType) {
        if (date == null)
            return "";
        if (StringUtils.isBlank(formatType))
            formatType = YMD_DASH;
        DateFormat sdf = new SimpleDateFormat(formatType);
        return sdf.format(date);
    }

    /**
     * 字符串转换为Date
     *
     * @param dateStr    - 时间字符串
     * @param formatType - 该字符串格式
     **/
    public static Date strToDate(String dateStr, String formatType) {
        try {
            if (StringUtils.isBlank(dateStr))
                return null;
            if (StringUtils.isBlank(formatType))
                formatType = YMD_DASH;
            DateFormat sdf = new SimpleDateFormat(formatType);
            return sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Title: getCurrentDate @Description: 返回当前时间 @param @return 设定文件 @return
     * String 返回类型 @throws
     */
    public static String getDateStr(Date date, String pattern) {
        if (StringUtils.isBlank(pattern)) {
            return format(date, YYDDMMHHMMSS);
        }
        return format(date, pattern);
    }

    /**
     * @Title: parseReturnTime @Description: 解析时间，空则返回当前时间 @param @param
     * date @param @return 设定文件 @return Date 返回类型 @throws
     */
    public static Date parseReturnTime(String date, String pattern) {
        if (date == null) {
            return new Date();
        } else {
            return parse(date, pattern);
        }
    }

    /**
     * 当期日期加一小时
     *
     * @param times
     * @return
     */
    public static Date addHours(Date times) {
        Calendar c = Calendar.getInstance();
        c.setTime(times); // 设置当前日期
        c.add(Calendar.HOUR, 1); // 日期分钟加1,Calendar.DATE(天),Calendar.HOUR(小时)
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        Date date = c.getTime(); // 结果
        return date;
    }

    /**
     * 获得当前系统Unix时间戳
     */
    public static Integer getNowUnixTimestamp() {
        Integer now = 0;
        Long time = System.currentTimeMillis() / 1000;
        now = time.intValue();
        return now;
    }

    /**
     * 获得当日系统Unix时间戳
     */
    public static Integer getIntradayUnixTimestamp() {
        long current = System.currentTimeMillis();
        long zero = current / (1000 * 3600 * 24) * (1000 * 3600 * 24) - TimeZone.getDefault().getRawOffset();
        return (int) (zero / 1000);
    }

    /**
     * Date转UnixTimestamp
     */
    public static Integer getUnixTimestamp(Date date) {
        if (date == null) {
            return null;
        }
        Long time = date.getTime();
        Long unixTimestampLong = (time / 1000);
        return unixTimestampLong.intValue();
    }

    /**
     * 指定日期起始时刻UnixTimestamp
     * <p>
     * 输入: Date
     * 输出: Date 凌晨 UnixTimestamp
     */
    public static Integer getDayBeginningUnixTimestamp(Date date) {
        if (date == null) {
            return null;
        }
        Date d = getDayBeginning(date);
        return getUnixTimestamp(d);
    }

    /**
     * int型时间戳转指定格式日期字符串
     */
    public static String formatUnixTimestamp(Integer unixTimestamp, String pattern) {
        if (unixTimestamp == null) {
            return "";
        }
        long timeInMillis = (long) unixTimestamp * 1000;
        Date d = new Date(timeInMillis);
        String format = DateFormatUtils.format(d, pattern);
        return format;
    }


    public static void main1(String[] args) {
        System.out.println(getDayBeginningUnixTimestamp(new Date()));
        System.out.println(str2UnixTimestamp("20170905", YMD));
    }

    /**
     * 日期字符串转unixTimestamp
     */
    public static Integer str2UnixTimestamp(String str, String pattern) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        Date date = DateUtils.parse(str, pattern);
        if (date == null) {
            return null;
        }
        return getUnixTimestamp(date);
    }


    /**
     * 获取Date所属月首日的Date
     */
    public static Date getMonthFirstDayByDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
        return cal.getTime();
    }

    /**
     * 获取Date所属月尾日日期
     */
    public static Date getMongthLastDayByDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        return cal.getTime();
    }

    /**
     * 返回date凌晨日期
     */
    public static Date getDayBeginning(Date date) {
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }
    
    /**
     * 返回date结尾日期
     */
    public static Date getDayEnding(Date date) {
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        return cal.getTime();
    }

    /**
     * 返回某一日期当月的日历日期列表
     * <p>
     * 如 2017/09/06
     * 则返回  2017/08/28 ~ 2017/10/01 之间的日期,包括 边际日期
     */
    public static List<Date> getCalendarDateList(Date date) {
        List<Date> dateList = new ArrayList<Date>();
        Date monthFirstDay = getMonthFirstDayByDate(date);
        Calendar monthFirstDayCal = convert(monthFirstDay);
        Date monthLastDay = getMongthLastDayByDate(date);
        Calendar monthLastDayCal = convert(monthLastDay);

        // 本月月尾向后推取最后一天
        Calendar cursor = (Calendar) monthLastDayCal.clone();
        while (cursor.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
            cursor.add(Calendar.DAY_OF_MONTH, 1);
        }
        Calendar lastDayCal = cursor;

        // 本月月首向前推取第一天
        cursor = (Calendar) monthFirstDayCal.clone();
        while (cursor.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
            cursor.add(Calendar.DAY_OF_MONTH, -1);
        }
        Calendar firstDayCal = cursor;

        for (; firstDayCal.compareTo(lastDayCal) <= 0; firstDayCal.add(Calendar.DATE, 1)) {
            dateList.add(firstDayCal.getTime());
        }
        return dateList;
    }

    public static void main2(String[] args) {
        Date d = DateUtils.parse("20171015", YMD);
        List<Date> dateList = getCalendarDateList(d);
        for (Date date : dateList) {
            System.out.println(format(date, YMD));
        }
    }

    /**
     * 当前日期加一天
     * 返回Integer
     */
    public static Integer getNextDay(Date date){
        date=getNextDate(date);
        Long time = date.getTime();
        Long unixTimestampLong = (time / 1000);
        return unixTimestampLong.intValue();
    }
    /**
     * 当前日期加一天
     * 返回Integer
     */
    public static Date getNextDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, +1); //今天的时间加一天
        date = calendar.getTime();
        return date;
    }

    /**
     * 获取两个日期间的日期列表,包括边界日期
     */
    public static List<Date> getDateListBetween(Date minDate, Date maxDate) {
        List<Date> dateList = new ArrayList<Date>();
        if (minDate == null || maxDate == null || minDate.compareTo(maxDate) > 0) {
            return dateList;
        }
        minDate = getDayBeginning(minDate);
        maxDate = getDayBeginning(maxDate);
        Calendar maxDateCal = convert(maxDate);
        Calendar cursor = convert(minDate);
        while (cursor.compareTo(maxDateCal) <= 0) {
            dateList.add(cursor.getTime());
            cursor.add(Calendar.DAY_OF_MONTH, 1);
        }
        return dateList;
    }

    public static void main3(String[] args) {
        Date d = DateUtils.parse("2017-10-01 12:00:00", YYDDMMHHMMSS);
        Date d2 = DateUtils.parse("2017-10-15 11:00:00", YYDDMMHHMMSS);
        List<Date> list = getDateListBetween(d, d2);
        for (Date date : list) {
            System.out.println(date);
        }
    }

    public static void main(String[] args) {
        String string=getString("20171123","yyyy年MM月dd日");
        System.out.print(string);
        /*Date minDate = DateUtils.getDayBeginning(new Date());
        Date maxDate = DateUtils.dayOffset(minDate, 7);
        System.out.println(minDate);
        System.out.println(maxDate);
        Date ending = DateUtils.getDayEnding(new Date());
        System.out.println(ending);*/
    }
}
